#include "types.h"
#include "params.h"
#include "kfunc.h"
#include "lock.h"
#include "memory.h"
#include "biscuit.h"
#include "file.h"
#include "kalloc.h"
#include "proc.h"
#include "util.h"

#define PACKED __attribute__((packed))
#define ELF_BUF_SIZ 0x40000U

#define MAX_IMGSZ (PGSZ * NR_PROCPGS)

struct file elfile;
extern char elfbuf[];

/*
 * Basic types
 */
typedef uint32 Elf32_Addr;
typedef uint32 Elf32_Off;
typedef uint16 Elf32_Section;
typedef uint16 Elf32_Versym;
typedef uint8 Elf_Byte;
typedef uint16 Elf32_Half;
typedef int32 Elf32_Sword;
typedef uint32 Elf32_Word;
typedef int64 Elf32_Sxword;
typedef uint64 Elf32_Xword;

/*
 * ELF header
 */
#define EI_NIDENT 16
typedef struct {
    uint8 e_ident[EI_NIDENT];
    uint16 e_types;
    uint16 e_machine;
    uint32 e_version;
    Elf32_Addr e_entry;
    Elf32_Off e_phoff;
    Elf32_Off e_shoff;
    uint32 e_flags;
    uint16 e_ehsize;
    uint16 e_phentsize;
    uint16 e_phnum;
    uint16 e_shentsize;
    uint16 e_shnum;
    uint16 e_shstrndx;
} PACKED Elf32_Ehdr;

/*
 * Program header
 */
typedef struct {
    uint32 p_type;
    Elf32_Off p_offset;
    Elf32_Addr p_vaddr;
    Elf32_Addr p_paddr;
    uint32 p_filesz;
    uint32 p_memsz;
    uint32 p_flags;
    uint32 p_align;
} PACKED Elf32_Phdr;

#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6

#define PF_NONE 0
#define PF_X 1
#define PF_W 2
#define PF_R 4

void
elfloaderinit () {
    bzero (&elfile, sizeof (struct file));
    elfile.kbuf = elfbuf;
    elfile.rptr = 0;
    elfile.name[0] = 0;
    sleep_lock(&elfile.lk);
}

int
elf_ok (Elf32_Ehdr* ehdr) {
    if (ehdr->e_ident[0] == 0x7f)
    if (ehdr->e_ident[1] == 'E')
    if (ehdr->e_ident[2] == 'L')
    if (ehdr->e_ident[3] == 'F')
    if (ehdr->e_ident[4] == 1) // 32
    if (ehdr->e_ident[5] == 1) // lsb
    if (ehdr->e_types == 2) // exec
    if (ehdr->e_machine == 243) // riscv
    return 1;
    return 0;
}

// return pc, -1 for error
unsigned int
loadelf (char * name, void * pgtb) {
    int fsz;
    int elptr = 0, currph = 0;
    Elf32_Ehdr ehdr;
    Elf32_Phdr phdr;
    elfile.kbuf = elfbuf;
    elfile.rptr = 0;
    elfile.name[0] = 0;
    //sleep_lock(&elfile.lk);
    strncpy (elfile.name, name, MAX_NAMESZ-1);
    fsz = readfile (&elfile, elfbuf, MAX_IMGSZ);
    if (fsz <= 0)
        goto error;
    //panic ("loadelf");
    if (!elf_ok((Elf32_Ehdr*)elfbuf))
        goto error;
    if (fsz >= MAX_IMGSZ-PGSZ*4)
        goto error;
    memcpy (&ehdr, elfbuf+elptr, sizeof (Elf32_Ehdr));
    elptr += sizeof (Elf32_Ehdr);
    for (currph = 0; currph < ehdr.e_phnum; currph++) {
        memcpy (&phdr, elfbuf+ehdr.e_phoff+(ehdr.e_phentsize * currph), ehdr.e_phentsize);
        vmwrite (pgtb, elfbuf + phdr.p_offset, phdr.p_vaddr, phdr.p_filesz);
    }
    return ehdr.e_entry;
    error:
    return -1;
}